Skip to main content

libobs_simple\sources\windows\sources/
game_capture.rs

1use libobs_simple_macro::obs_object_impl;
2#[cfg(feature = "window-list")]
3use libobs_window_helper::{get_all_windows, WindowInfo, WindowSearchMode};
4use libobs_wrapper::{
5    data::{ObsObjectBuilder, ObsObjectUpdater, StringEnum},
6    sources::{ObsSourceBuilder, ObsSourceRef},
7    utils::ObsError,
8};
9
10use super::{ObsHookRate, ObsWindowPriority};
11use crate::{define_object_manager, sources::macro_helper::impl_custom_source};
12use crate::{
13    error::ObsSimpleError,
14    sources::windows::{ObsHookableSourceSignals, ObsHookableSourceTrait},
15};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18/// Describes the capture mode of the game capture source.
19pub enum ObsGameCaptureMode {
20    /// Captures any fullscreen application
21    Any,
22    /// Captures a specific window, specified under the `window` property
23    CaptureSpecificWindow,
24    /// CApture the foreground window when a hotkey is pressed
25    CaptureForegroundWindow,
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum ObsGameCaptureRgbaSpace {
30    /// sRGB color space
31    SRgb,
32    /// Rec. 2100 (PQ)
33    RGBA2100pq,
34}
35
36impl StringEnum for ObsGameCaptureRgbaSpace {
37    fn to_str(&self) -> &str {
38        match self {
39            ObsGameCaptureRgbaSpace::SRgb => "sRGB",
40            ObsGameCaptureRgbaSpace::RGBA2100pq => "Rec. 2100 (PQ)",
41        }
42    }
43}
44
45impl StringEnum for ObsGameCaptureMode {
46    fn to_str(&self) -> &str {
47        match self {
48            ObsGameCaptureMode::Any => "any_fullscreen",
49            ObsGameCaptureMode::CaptureSpecificWindow => "window",
50            ObsGameCaptureMode::CaptureForegroundWindow => "hotkey",
51        }
52    }
53}
54
55define_object_manager!(
56    #[derive(Debug)]
57    /// A source to capture a game or fullscreen application.
58    ///
59    ///
60    /// Use `GameCaptureSourceBuilder::get_windows` to get a list of windows that can be captured (feature `window-list` needs to be enabled).
61    /// Requires OBS to be running with administrator privileges to capture certain games.
62    ///
63    /// ## Important Notice
64    /// This source fails to capture if another instance (OBS studio, another instance of your program, etc.) has a game capture source for the same game/application active.
65    /// If the window can be captured can be checked using `GameCaptureSourceBuilder::is_window_in_use_by_other_instance` (feature `window-list` needs to be enabled).
66    struct GameCaptureSource("game_capture", *mut libobs::obs_source) for ObsSourceRef {
67        /// Sets the capture mode for the game capture source. Look at doc for `ObsGameCaptureMode`
68        #[obs_property(type_t = "enum_string")]
69        capture_mode: ObsGameCaptureMode,
70
71        /// Sets the window to capture.
72        ///
73        /// # Arguments
74        ///
75        /// * `window` - The window to capture, represented as `ObsString`. Must be in the format of an obs window id
76        ///
77        /// # Returns
78        ///
79        /// The updated `WindowCaptureSourceBuilder` instance.
80        #[obs_property(type_t = "string", settings_key = "window")]
81        window_raw: String,
82
83        #[obs_property(type_t = "enum")]
84        /// Window Match Priority
85        priority: ObsWindowPriority,
86
87        #[obs_property(type_t = "bool")]
88        /// SLI/Crossfire Capture Mode (Slow)
89        sli_compatability: bool,
90
91        #[obs_property(type_t = "bool")]
92        /// Whether the cursor should be captured
93        capture_cursor: bool,
94
95        #[obs_property(type_t = "bool")]
96        /// If transparency of windows should be allowed
97        allow_transparency: bool,
98
99        #[obs_property(type_t = "bool")]
100        /// Premultiplied Alpha
101        premultiplied_alpha: bool,
102
103        /// Limit capture framerate
104        #[obs_property(type_t = "bool")]
105        limit_framerate: bool,
106
107        /// Capture third party overlays (such as steam overlays)
108        #[obs_property(type_t = "bool")]
109        capture_overlays: bool,
110
111        /// Use anti-cheat compatibility hook
112        #[obs_property(type_t = "bool")]
113        anti_cheat_hook: bool,
114
115        /// Hook rate (Ranging from slow to fastest)
116        #[obs_property(type_t = "enum")]
117        hook_rate: ObsHookRate,
118
119        /// The color space to capture in
120        #[obs_property(type_t = "enum_string")]
121        rgb10a2_space: ObsGameCaptureRgbaSpace,
122
123        /// Whether to capture audio from window source (BETA) <br>
124        /// When enabled, creates an "Application Audio Capture" source that automatically updates to the currently captured window/application. <br>
125        /// Note that if Desktop Audio is configured, this could result in doubled audio.
126        capture_audio: bool,
127    }
128);
129
130#[cfg(feature = "window-list")]
131impl GameCaptureSourceBuilder {
132    /// Gets a list of windows that can be captured by this source.
133    pub fn get_windows(mode: WindowSearchMode) -> Result<Vec<WindowInfo>, ObsSimpleError> {
134        get_all_windows(mode)
135            .map(|e| e.into_iter().filter(|x| x.is_game).collect::<Vec<_>>())
136            .map_err(ObsSimpleError::WindowHelperError)
137    }
138
139    /// Checks if a window with the given process ID can be captured by this source.
140    /// This does not guarantee that the window can be captured, only that it is not black
141    pub fn is_window_in_use_by_other_instance(window_pid: u32) -> std::io::Result<bool> {
142        use libobs_window_helper::is_window_in_use_by_other_instance;
143
144        is_window_in_use_by_other_instance(window_pid)
145    }
146
147    /// Sets the window to capture.
148    ///
149    /// # Arguments
150    ///
151    /// * `window` - The window to capture. A list of available windows can be retrieved using `GameCaptureSourceBuilder::get_windows`
152    ///
153    /// # Returns
154    ///
155    /// The updated `GameCaptureSourceBuilder` instance.
156    pub fn set_window(self, window: &WindowInfo) -> Self {
157        self.set_window_raw(window.obs_id.as_str())
158    }
159}
160
161#[obs_object_impl]
162impl GameCaptureSource {
163    pub fn set_capture_audio(mut self, capture_audio: bool) -> Result<Self, ObsSimpleError> {
164        use crate::sources::windows::audio_capture_available;
165
166        if capture_audio && !audio_capture_available(self.runtime())? {
167            return Err(ObsSimpleError::FeatureNotAvailable(
168                "Game Audio Capture is not available on this system",
169            ));
170        }
171
172        self.get_settings_updater()
173            .set_bool_ref("capture_audio", capture_audio);
174
175        Ok(self)
176    }
177}
178
179impl_custom_source!(
180    GameCaptureSource,
181    ObsHookableSourceSignals,
182    NO_SPECIFIC_SIGNALS_FUNCTION
183);
184
185impl ObsHookableSourceTrait for GameCaptureSource {
186    fn source_specific_signals(&self) -> std::sync::Arc<ObsHookableSourceSignals> {
187        self.source_specific_signals.clone()
188    }
189}
190
191// Custom made signals for the game capture and implementation
192
193impl ObsSourceBuilder for GameCaptureSourceBuilder {
194    type T = GameCaptureSource;
195
196    fn build(self) -> Result<Self::T, ObsError>
197    where
198        Self: Sized,
199    {
200        let runtime = self.runtime.clone();
201        let s = self.object_build()?;
202
203        let source = ObsSourceRef::new_from_info(s, runtime)?;
204        GameCaptureSource::new(source)
205    }
206}